/*
 * $Id: ServiceLocator.java,v 1.11 2010/03/03 20:34:34 navdeep_mahajan Exp $
 * 
 * Copyright (C) 2006 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.common.util;

/**
 * Purpose:
 * <ul>
 * <li>The class is used to cache the initial context for saving JNDI look up
 * time</li>
 * </ul>
 * 
 * @author Navdeep Mahajan
 */
// Java utility class
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.exceptions.ServiceLocatorException;

/**
 * This class is implemented as a singleton class and is a central place for
 * looking up objects in the JNDI tree.
 * 
 * @author 212042764
 */
public class ServiceLocator {
	// Cache of objects in JNDI tree
	private Map<String, Map<Object, Object>> homeCache;

	// No Initial Context provider identifier
	private static final String DEFAULT_CONTEXT = "DEFAULT_CONTEXT";

	// Context Hash
	private Map<String, InitialContext> contextHash;

	private Map<String, String> environment = null;

	/**
	 * Private constructor, which initializes all the tables and the default
	 * InitialContext.
	 * 
	 * @throws NamingException
	 */
	public ServiceLocator() {
		homeCache = Collections
				.synchronizedMap(new HashMap<String, Map<Object, Object>>());
		contextHash = Collections
				.synchronizedMap(new HashMap<String, InitialContext>());
	}

	/**
	 * Method to return an object in the default JNDI context, with the supplied
	 * JNDI name.
	 * 
	 * @param <b>jndiName </b> The JNDI name
	 * 
	 * @return <b>Object</b> The object in the JNDI tree for this name.
	 * 
	 * @throws <b>UtilityException </b> Exception this method can throw
	 */
	public Object getService(String jndiName) throws ServiceLocatorException {
		try {
			if (!homeCache.containsKey(DEFAULT_CONTEXT)
					|| homeCache.get(DEFAULT_CONTEXT) == null
					|| !(homeCache.get(DEFAULT_CONTEXT)).containsKey(jndiName)) {
				// If the service is not in the cache, get the object for the
				// supplied jndi name and put it in the cache
				if (!homeCache.containsKey(DEFAULT_CONTEXT)) {
					homeCache.put(DEFAULT_CONTEXT, Collections
							.synchronizedMap(new HashMap<Object, Object>()));
				}
				Map<Object, Object> cacheMap = homeCache.get(DEFAULT_CONTEXT);
				InitialContext context = null;
				if (contextHash.get(DEFAULT_CONTEXT) != null) {
					context = (InitialContext) contextHash.get(DEFAULT_CONTEXT);
				} else {
					context = getInitialContext(null);
					contextHash.put(DEFAULT_CONTEXT, context);
				}
				cacheMap.put(jndiName, context.lookup(jndiName));
				homeCache.put(DEFAULT_CONTEXT, cacheMap);
			}
		} catch (NamingException namingException) {
			throw new ServiceLocatorException(
					new StringBuffer()
							.append(
									"Exception thrown from getService method of ServiceLocator class. Exception : ")
							.append(namingException.getMessage()).toString(),
					namingException);
		} catch (SecurityException securityException) {
			throw new ServiceLocatorException(
					new StringBuffer()
							.append(
									"Exception thrown from getService method of ServiceLocator class. Exception : ")
							.append(securityException.getMessage()).toString(),
					securityException);
		}
		// Return object from cache
		return homeCache.get(DEFAULT_CONTEXT).get(jndiName);
	}

	/**
	 * Method to return an object in the default JNDI context, with the supplied
	 * JNDI name.
	 * 
	 * @param <b>jndiName </b> The JNDI name
	 * 
	 * @return <b>Object</b> The object in the JNDI tree for this name.
	 * 
	 * @throws <b>UtilityException </b> Exception this method can throw
	 */
	public Object getService(String providerURL, String jndiName)
			throws ServiceLocatorException {
		if (ASCUtil.isNullOrEmpty(providerURL)) {
			return getService(jndiName);
		}
		try {
			if (!homeCache.containsKey(providerURL)
					|| homeCache.get(providerURL) != null
					|| !(homeCache.get(providerURL)).containsKey(jndiName)) {
				// If the service is not in the cache, get the object for the
				// supplied jndi name and put it in the cache
				if (!homeCache.containsKey(providerURL)) {
					homeCache.put(providerURL, Collections
							.synchronizedMap(new HashMap<Object, Object>()));
				}
				Map<Object, Object> cacheMap = homeCache.get(providerURL);
				InitialContext context = null;
				if (contextHash.get(providerURL) != null) {
					context = (InitialContext) contextHash.get(providerURL);
				} else {
					context = getInitialContext(providerURL);
					contextHash.put(providerURL, context);
				}
				cacheMap.put(jndiName, context.lookup(jndiName));
				homeCache.put(providerURL, cacheMap);
			}
		} catch (NamingException namingException) {
			throw new ServiceLocatorException(
					"Exception thrown from getService "
							+ "method of ServiceLocator class : ",
					namingException);
		} catch (SecurityException securityException) {
			throw new ServiceLocatorException(
					"Exception thrown from from getService "
							+ "method of ServiceLocator class : ",
					securityException);
		}
		// Return object from cache
		return homeCache.get(providerURL).get(jndiName);
	}

	/**
	 * If provider URL is specified use the same to create the initial Context
	 * otherwise return the default context
	 * 
	 * @param jndiHost
	 * @return
	 * @throws NamingException
	 */
	private InitialContext getInitialContext(String providerURL)
			throws NamingException {
		String methodName = "getInitialContext";
		// If provider URL is specified use the same to create the initial
		// Context otherwise return the default context
		Hashtable<String, String> env = new Hashtable<String, String>();
		if (!ASCUtil.isNullOrEmptyMap(environment)) {
			ASCLogger.debug(this.getClass(), methodName,
					"Enviornment as read from conf file:" + environment);
			env.putAll(environment);
		}
		if (!ASCUtil.isNullOrEmpty(providerURL)) {
			ASCLogger.debug(this.getClass(), methodName,
					"Provider URL for initial context lookup:" + providerURL);
			env.put(Context.PROVIDER_URL, providerURL);
		}
		ASCLogger.debug(this.getClass(), methodName,
				"Looking up initial context using enviornment as:" + env);
		return new InitialContext(env);
	}

	/**
	 * Closes context which may have been opened
	 * 
	 * @param ctx
	 */
	public void closeContext(InitialContext ctx) {
		if (!ASCUtil.isNullOrEmptyObject(ctx)) {
			try {
				ctx.close();
			} catch (NamingException namingException) {
				ctx = null;
			}
		}
	}

	/**
	 * @param environment
	 *            the environment to set
	 */
	public void setEnvironment(Map<String, String> environment) {
		this.environment = environment;
	}
}
